/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：
* CreateTime：2020-05-25 14:05:29
* Version：1.0
* Modify Recorder：
*************************************************/

using System;
using UnityEngine;
using UnityEngine.Events;

namespace TinyToolKit
{
    [Serializable]
    public class RaycastEvent : UnityEvent<RaycastHit> { }

    public enum UpdateEvent
    {
        Update,
        LateUpdate
    }

    public class MouseRaycast : MonoBehaviour
    {
        public Camera mainCam;
        public UpdateEvent updateEvent = UpdateEvent.LateUpdate;
        public LayerMask layerMask = -1;
        public float rayLength = -1;
        public RaycastEvent onEnter;
        public RaycastEvent onHover; 
        public RaycastEvent onExit;

        private void Start()
        {
            if (mainCam == null)
                mainCam = Camera.main;
        }

        private void Update()
        {
            ExecuteRaycast(UpdateEvent.Update);
        }
        
        private void LateUpdate()
        {
            ExecuteRaycast(UpdateEvent.LateUpdate);
        }

        RaycastHit newHitInfo;
        RaycastHit oldHitInfo; 
        
        private void ExecuteRaycast(UpdateEvent curEvent)
        {
            if (curEvent != updateEvent || mainCam == null) return;
            Vector2 mousePos = Input.mousePosition;
            if (mousePos.x < 0 || mousePos.x > Screen.width || mousePos.y < 0 || mousePos.y > Screen.height) return;
            
            Ray ray = mainCam.ScreenPointToRay(Input.mousePosition);
            bool isRayCast = Physics.Raycast(ray, out newHitInfo, rayLength >= 0 ? rayLength : Mathf.Infinity, layerMask.value);
            if (isRayCast)
            {
                if (newHitInfo.collider != oldHitInfo.collider)
                {
                    onEnter?.Invoke(newHitInfo);
                    if(oldHitInfo.collider)
                        onExit?.Invoke(oldHitInfo);
                    
                    oldHitInfo = newHitInfo;
                }
                else
                {
                    onHover?.Invoke(newHitInfo); 
                }
            }
            else if(oldHitInfo.collider)
            {
                onExit?.Invoke(oldHitInfo);
                oldHitInfo = newHitInfo;
            }
        }
    }
}

